#include "drv_rtc.h"
#include "bsp_rtc.h"
#if (CONFIG_OS_ENABLE == 1)
#include "os_sal.h"
#endif


/* rtc时钟驱动 */



/* define */
#define RTC_UNIXCNT_1900_1970		0x83AA7E80 		//1900到1970的总时间数(秒)
#define RTC_UNIXCNT_1970_2000		0x386D4380 		//1900到1970的总时间数(秒)
#define RTC_UNIXCNT_1900_2000		0xBC17C200		//1900到2000的总时间数(秒)
#define RTC_UNIXCNT_TIMEZONE		28800			//时区偏差(8小时 8*60*60)
#define RTC_UNIXCNT_YEAR			31536000		//一年总时间数(365*24*60*60 秒)
#define RTC_UNIXCNT_DATE			86400			//一天总时间数(24*60*60 秒)
#define RTC_UNIXCNT_HOUR			3600			//一小时总时间数(60*60 秒)

/* static var */
static const uint8_t rtc_month_tbl[12]={31,28,31,30,31,30,31,31,30,31,30,31};

#if (CONFIG_OS_ENABLE == 1)
static os_mutex_handle_t rtc_mutex = NULL;
#endif


/* static func */


/**
 * @brief 判断是否是闰年函数
 *        月份   1  2  3  4  5  6  7  8  9  10 11 12
 *        闰年   31 29 31 30 31 30 31 31 30 31 30 31
 *        非闰年 31 28 31 30 31 30 31 31 30 31 30 31
 * 
 * @param year 年份(2000-2099)
 * @return uint8_t 
 */
static uint8_t drv_rtc_is_leap_year(uint16_t year)
{
	if(year%4==0) //必须能被4整除
	{ 
		if(year%100==0) 
		{ 
			if(year%400==0)return 1;//如果以00结尾,还要能被400整除 	   
			else return 0;   
		}else return 1;   
	}else return 0;	
}

static uint8_t drv_rtc_get_max_date(uint16_t year, uint8_t month)
{ 
	uint8_t md = 0;
	switch(month)
	{
		case 2:
			if(drv_rtc_is_leap_year(year)) md=29;
			else md=28;
			break;
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:
			md=31;
			break;
		case 4:
		case 6:
		case 9:
		case 11:
			md=30;
			break;
	}
	return(md);
}

static uint8_t drv_rtc_time_check_lowful(_drv_rtc_time_t *obj)
{
    uint8_t ret = 0;
    uint8_t tmp;

    if (obj->year<2000 || obj->year > 2099)
    {
        obj->year = 2000;
        ret |= (1<<0);
    }

    if (obj->month == 0 || obj->month > 12)
    {
        obj->month = 1;
        ret |= (1<<1);
    }

    tmp = drv_rtc_get_max_date(obj->year, obj->month);
    if (obj->date == 0 || obj->date > tmp)
    {
        obj->date = 1;
        ret |= (1<<2);
    }

    if (obj->hour > 23)
    {
        obj->hour = 0;
        ret |= (1<<3);
    }

    if (obj->min > 59)
    {
        obj->min = 0;
        ret |= (1<<4);
    }

    if (obj->sec > 59)
    {
        obj->sec = 0;
        ret |= (1<<5);
    }

    return ret;
}

static uint16_t drv_rtc_get_date_of_year(const _drv_rtc_time_t *obj)
{
    uint16_t cnt;
    uint8_t i;

    cnt = 0;
    for (i = 1; i < obj->month; i++)
    {
        cnt += drv_rtc_get_max_date(obj->year, i);
    }

    return cnt;
}

/* export func */

/**
 * @brief RTC初始化
 * 
 */
void drv_rtc_init(void)
{
    bsp_rtc_init();

#if (CONFIG_OS_ENABLE == 1)
    rtc_mutex = os_mutex_create();
#endif
}


void drv_rtc_lock(void)
{
#if (CONFIG_OS_ENABLE == 1)
    os_mutex_take(rtc_mutex, OS_WAIT_MAX);
#endif
}

void drv_rtc_unlock(void)
{
#if (CONFIG_OS_ENABLE == 1)
    os_mutex_release(rtc_mutex);
#endif
}

/**
 * @brief RTC时间获取
 * 
 * @param obj 
 */
void drv_rtc_get(_drv_rtc_time_t *obj)
{
    _bsp_rtc_time_t tm;

    bsp_rtc_get_time(&tm);

    obj->year = tm.year + 2000;
    obj->month = tm.month;
    obj->date = tm.date;
    obj->day = tm.day;
    obj->hour = tm.hour;
    obj->min = tm.min;
    obj->sec = tm.sec;
    obj->yday = drv_rtc_get_date_of_year(obj);
}

/**
 * @brief RTC时间设置
 * 
 * @param obj 
 */
void drv_rtc_set(_drv_rtc_time_t *obj)
{
    _bsp_rtc_time_t tm;

    drv_rtc_time_check_lowful(obj);

    tm.year = obj->year - 2000;
    tm.month = obj->month;
    tm.date = obj->date;
    tm.day = obj->day;
    tm.hour = obj->hour;
    tm.min = obj->min;
    tm.sec = obj->sec;
    bsp_rtc_set_time(&tm);
}

/**
 * @brief 判断当前时间范围是否有效
 * 
 * @param obj 
 * @return uint8_t 
 */
uint8_t drv_rtc_check(_drv_rtc_time_t *obj)
{
    uint8_t rval=0;
    uint8_t maxdate;

    if(obj->year<2000 || obj->year>2099) rval|=1<<0;
    if(obj->month==0 || obj->month>12) rval|=1<<1;
    if(obj->date==0 || obj->date>31) rval|=1<<2;
    if(obj->hour>59) rval|=1<<3;
    if(obj->min>59) rval|=1<<4;
    if(obj->sec>59) rval|=1<<5;

    if(rval==0)
    {
        maxdate = drv_rtc_get_max_date(obj->year, obj->month);
        if(obj->date>maxdate) obj->date=maxdate;
    }

    return rval;
}


/**
 * @brief 根据UNIX时间戳计算对应时间(从1970起算)
 * 
 * @param obj 
 * @param unix UNIX时间戳
 * @return uint8_t 
 */
uint8_t drv_rtc_get_time_form_unix(_drv_rtc_time_t *obj, uint32_t unix)
{
    uint16_t result;
    uint32_t temp;

    if (unix < RTC_UNIXCNT_1970_2000) // 在2000年前的时间
    {
        result = 1970;
    }
    else
    {
        result = 2000;
        unix -= RTC_UNIXCNT_1970_2000;
    }
    unix += RTC_UNIXCNT_TIMEZONE; // 时区(北京时间 +8:00)

    /* 年计算 */
    while (1)
    {
        temp = RTC_UNIXCNT_YEAR;
        if (drv_rtc_is_leap_year(result)) temp += RTC_UNIXCNT_DATE;

        if (unix >= temp)
        {
            unix -= temp;
            result++;
        }
        else
        {
            break;
        }
    }
    obj->year = result;

    /* 月份 */
    result = 1;
    while (1)
    {
        if (drv_rtc_is_leap_year(obj->year) && result == 2)
        {
            temp = 29 * RTC_UNIXCNT_DATE; // 闰年2月
        }
        else
        {
            temp = rtc_month_tbl[result-1] * RTC_UNIXCNT_DATE;
        }
        if (unix >= temp)
        {
            unix -= temp;
            result++;
        }
        else
        {
            break;
        }
    }
    obj->month = result;

    /* 日期计算 */
    obj->date = unix / RTC_UNIXCNT_DATE + 1;

    temp = unix % RTC_UNIXCNT_DATE;
    obj->hour = temp / RTC_UNIXCNT_HOUR;       //
    obj->min = (temp % RTC_UNIXCNT_HOUR) / 60; //
    obj->sec = temp % 60;                      //

    return 0;
}

/**
 * @brief 根据当前rtc时间计算UNIX时间戳
 * 
 * @param obj 
 * @param unix 
 * @return uint8_t 
 */
uint8_t drv_rtc_get_unix_form_time(_drv_rtc_time_t *obj, uint32_t *unix)
{
    uint8_t ret;
    uint8_t i;
    uint16_t temp;
    uint32_t rval = 0;

    if (obj == NULL) return 0xFF;
    ret = drv_rtc_check(obj);
    if (ret == 0)
    {
        /* year */
        rval = RTC_UNIXCNT_1970_2000;
        for (temp = 2000; temp < obj->year; temp++)
        {
            rval += RTC_UNIXCNT_YEAR;
            if (drv_rtc_is_leap_year(temp))
            {
                rval += RTC_UNIXCNT_DATE;
            }
        }

        /* month */
        for (i = 1; i < obj->month; i++)
        {
            if (drv_rtc_is_leap_year(obj->year) && i == 2)
            {
                rval += 29 * RTC_UNIXCNT_DATE; // 闰年2月
            }
            else
            {
                rval += (rtc_month_tbl[i - 1] * RTC_UNIXCNT_DATE);
            }
        }

        rval += (obj->date - 1) * RTC_UNIXCNT_DATE; /* date */
        rval += obj->hour * RTC_UNIXCNT_HOUR;       /* hour */
        rval += obj->min * 60;                      /* min */
        rval += obj->sec;                           /* sec */

        rval -= 8 * RTC_UNIXCNT_HOUR; // 时区(北京时间 +8:00)
    }

    if (unix != NULL) *unix = rval;

    return ret;
}

#if (CONFIG_SHELL_ENABLE == 1)

#include "shell.h"

int rtc(int argc, char **argv)
{
    _drv_rtc_time_t tm;
    int year, month, date, hour, min, sec;
    int ret;

    if (argc == 1)
    {
        /* get rtc time */
        drv_rtc_get(&tm);
        printf("RTC: %4d/%02d/%02d %02d:%02d:%02d\r\n", 
                tm.year, tm.month, tm.date, tm.hour, tm.min, tm.sec);
    }
    else if (argc == 2)
    {
        /* set rtc time */
        ret = sscanf(argv[1], "%d/%d/%d-%d:%d:%d", &year, &month, &date, &hour, &min, &sec);
        if (ret != 6)
        {
            printf("rtc format err!\r\n");
            return -1;
        }
        tm.year = year;
        tm.month = month;
        tm.date = date;
        tm.hour = hour;
        tm.min = min;
        tm.sec = sec;
        ret = drv_rtc_check(&tm);
        if (ret)
        {
            printf("rtc data err!\r\n");
            return -1;
        }
        printf("setting rtc...\r\n");
        drv_rtc_set(&tm);

        memset(&tm, 0x00, sizeof(_drv_rtc_time_t));

        drv_rtc_get(&tm);
        printf("reread rtc: %4d/%02d/%02d %02d:%02d:%02d\r\n", 
                tm.year, tm.month, tm.date, tm.hour, tm.min, tm.sec);
    }
    else 
    {
        printf("usage: %s yyyy/mm/dd-hh:mm:ss \r\n", argv[0]);
        return -1;
    }
    
    return 0;
}
SHELL_CMD_EXPORT(rtc, get or set rtc time);

int bkr(int argc, char **argv)
{
    int reg;
    uint32_t val;

    if (argc == 2)
    {
        reg = atoi(argv[1]);
        if (reg > 32) reg = 0;

        val = bsp_rtc_bkr_read(reg);
        printf("reg %d = 0x%08X\r\n", reg, val);
    }
    else if (argc == 3)
    {
        printf("bkr write not support yet\r\n");
    }
    else
    {
        printf("usage: %s [reg]\r\n", argv[0]);
    }

    return 0;
}
SHELL_CMD_EXPORT(bkr, read or write backup-register);

#endif



